package top.hypnos.bigdata.filesystem.command;

import top.hypnos.bigdata.filesystem.Command;
import top.hypnos.bigdata.filesystem.MemDataTree;
import top.hypnos.bigdata.filesystem.record.ObjectInputStream;
import top.hypnos.bigdata.filesystem.record.ObjectOutputStream;

import java.io.IOException;

public class CommandImpl<T extends CommandExecutor> implements Command {

    private int txnId;

    private String path;

    private byte[] data;

    private T executor;

    public CommandImpl() {
        this(0, null, null, null);
    }

    public CommandImpl(final int txnId,
                       final String path,
                       final Class<T> executorType) {
        this(txnId, path, new byte[0], executorType);
    }

    public CommandImpl(final int txnId,
                       final String path,
                       final byte[] data,
                       final Class<T> executorType) {
        this.txnId = txnId;
        this.path = path;
        this.data = data;
        if (executorType != null) {
            try {
                this.executor = executorType.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public int getTxnId() {
        return txnId;
    }

    public String getPath() {
        return path;
    }

    public byte[] getData() {
        return data;
    }

    @Override
    public void preExecute(MemDataTree dataTree) throws CommandException {
        executor.preExecute(dataTree, this);
    }

    public void commit() {
        executor.commit();
    }

    @Override
    public void serialize(final ObjectOutputStream output) throws IOException {
        output.writeInt(txnId);
        output.writeString(path);
        output.writeBytes(data);
        if (executor == null) {
            output.writeString("-");
        } else {
            final var type = executor.getClass();
            if (type.equals(CreateNodeCommandExecutor.class)) {
                output.writeString("C");
            } else if (type.equals(DeleteNodeCommandExecutor.class)) {
                output.writeString("D");
            } else {
                output.writeString("-");
            }
        }
    }

    @Override
    public void deserialize(final ObjectInputStream input) throws IOException {
        txnId = input.readInt();
        path = input.readString();
        data = input.readBytes();
        final var executorType = input.readString();
        try {
            switch (executorType) {
                case "-":
                    executor = null;
                    break;
                case "C":
                    executor = (T) new CreateNodeCommandExecutor();
                    break;
                case "D":
                    executor = (T) new DeleteNodeCommandExecutor();
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public T getExecutor() {
        return executor;
    }
}
